module axi_bridge(
    input clk,
    input resetn,
//sram interface
    //inst sram
    input inst_sram_req,
    input inst_sram_wr,
    input [1:0] inst_sram_size,
    input [3:0] inst_sram_wstrb,
    input [31:0] inst_sram_addr,
    input [31:0] inst_sram_wdata,
    output inst_sram_addr_ok,
    output inst_sram_data_ok,
    output [31:0] inst_sram_rdata,
    //data sram
    input data_sram_req,
    input data_sram_wr,
    input [1:0] data_sram_size,
    input [31:0] data_sram_addr,
    input [3:0] data_sram_wstrb,
    input [31:0] data_sram_wdata,
    output data_sram_addr_ok,
    output data_sram_data_ok,
    output [31:0] data_sram_rdata,
//axi interface
    //read acquire
    output [ 3:0] arid, //inst: 0, data: 1
    output [31:0] araddr,
    output [ 7:0] arlen, //set to 0
    output [ 2:0] arsize,
    output [ 1:0] arburst, //set to 2'b01
    output [ 1:0] arlock, //set to 0
    output [ 3:0] arcache, //set to 0
    output [ 2:0] arprot, //set to 0
    output        arvalid,
    input         arready,
    //read
    input  [ 3:0] rid, //inst: 0, data: 1
    input  [31:0] rdata,
    input  [ 1:0] rresp, //ignored
    input         rlast, //ignored
    input         rvalid,
    output         rready,
    //write acquire
    output [ 3:0] awid,
    output [31:0] awaddr,
    output [ 7:0] awlen, //set to 0
    output [ 2:0] awsize,
    output [ 1:0] awburst, //set to 2'b01
    output [ 1:0] awlock, //set to 0
    output [ 3:0] awcache, //set to 0
    output [ 2:0] awprot, //set to 0
    output        awvalid,
    input         awready,
    //write data
    output [ 3:0] wid, //set to 1
    output [31:0] wdata,
    output [ 3:0] wstrb,
    output        wlast, //set to 1
    output        wvalid,
    input         wready,
    //write feedback
    input  [ 3:0] bid, //ignored
    input  [ 1:0] bresp, //ignored
    input         bvalid,
    output        bready
);

wire [2:0] asize_data;
wire [2:0] asize_inst;
assign asize_inst = {inst_sram_size[1:0], ~(|inst_sram_size[1:0])};
assign asize_data = {data_sram_size[1:0], ~(|data_sram_size[1:0])};

/******************** DECALRATION ********************/
    reg [7:0] r_cur_state;
    reg [7:0] r_next_state;
    reg [7:0] w_cur_state;
    reg [7:0] w_next_state;
    //read
    reg [3:0] arid_r;
    reg [31:0] araddr_r;
    reg [2:0] arsize_r;
    reg arvalid_r;
    wire read_after_write;
    //write
    reg [3:0] awid_r;
    reg [31:0] awaddr_r;
    reg [2:0] awsize_r;
    reg awvalid_r;
    reg [31:0] wdata_r;
    reg [3:0] wstrb_r;
    wire write_after_read;
    //cpu
    wire cpu_inst_read_req;
    wire cpu_inst_write_req;
    wire cpu_data_read_req;
    wire cpu_data_write_req;
/******************** READ STATE MACHINE ********************/
    //define state
    localparam READ_INIT = 8'h01,
               READ_INST_REQ = 8'h02,
               READ_DATA_REQ = 8'h04,
               READ_RECV = 8'h08;
    //upgrade current reading state
    always @(posedge clk) begin
        if(!resetn)
            r_cur_state <= READ_INIT;
        else
            r_cur_state <= r_next_state;
    end
    //generate next state
    always @(*) begin
        case(r_cur_state)
            READ_INIT: begin
                if(cpu_inst_read_req & arvalid & ~arready)
                    r_next_state = READ_INST_REQ;
                else if(cpu_data_read_req & arvalid & ~arready)
                    r_next_state = READ_DATA_REQ;
                else if((cpu_inst_read_req | cpu_data_read_req) 
                        & arvalid & arready)
                    r_next_state = READ_RECV;
                else
                    r_next_state = READ_INIT;
            end
            READ_INST_REQ, READ_DATA_REQ: begin
                if(arvalid_r & arready)
                    r_next_state = READ_RECV;
                else
                    r_next_state = READ_INST_REQ;
            end
            READ_RECV: begin
                if(rready & rvalid)
                    r_next_state = READ_INIT;
                else 
                    r_next_state = READ_RECV;
            end
            default: 
                r_next_state = READ_INIT;
        endcase
    end

    always @(posedge clk) begin
        if(~resetn) begin
            arid_r <= 4'b0;
            arvalid_r <= 1'b0;
            arsize_r <= 3'b0;
            araddr_r <= 32'b0;
        end
        else if(r_cur_state[0] & cpu_inst_read_req & arvalid) begin
            arid_r <= 4'b0;
            arvalid_r <= arvalid;
            arsize_r <= asize_inst;
            araddr_r <= inst_sram_addr;
        end
        else if(r_cur_state[0] & cpu_data_read_req & arvalid) begin
            arid_r <= 4'b1;
            arvalid_r <= arvalid;
            arsize_r <= asize_data;
            araddr_r <= data_sram_addr;
        end
        else if(r_cur_state[3] & rready & rvalid) begin
            arid_r <= 4'b0;
            arvalid_r <= 1'b0;
            arsize_r <= 3'b0;
            araddr_r <= 32'b0;
        end
    end

/******************** READ COMBINATIONAL LOGIC ********************/

    //read acquire
    assign arid = {4{r_cur_state[1] | r_cur_state[2]}} & arid_r
                | {4{r_cur_state[0] & cpu_inst_read_req}} & 4'b0
                | {4{r_cur_state[0] & cpu_data_read_req & ~cpu_inst_read_req}} & 4'b1;
    assign araddr = {32{r_cur_state[1] | r_cur_state[2]}} & araddr_r
                  | {32{r_cur_state[0] & cpu_inst_read_req}} & inst_sram_addr
                  | {32{r_cur_state[0] & cpu_data_read_req & ~cpu_inst_read_req}} & data_sram_addr;
    assign arlen = 8'b0;
    assign arsize =  {3{r_cur_state[1] | r_cur_state[2]}} & arsize_r
                  | {3{r_cur_state[0] & cpu_inst_read_req}} & asize_inst
                  | {3{r_cur_state[0] & cpu_data_read_req & ~cpu_inst_read_req}} & asize_data;
    assign arburst = 2'b01;
    assign arlock = 2'b0;
    assign arcache = 4'b0;
    assign arprot = 3'b0;
    assign arvalid = (cpu_inst_read_req | cpu_data_read_req)
                   & (r_cur_state[0] | r_cur_state[1] | r_cur_state[2]) & ~read_after_write;
    //read
    assign rready = r_cur_state == READ_RECV;
    //judge read after write
    assign read_after_write = (cpu_inst_read_req & (awaddr_r[31:2] == inst_sram_addr[31:2]))
                            | (cpu_data_read_req & (awaddr_r[31:2] == data_sram_addr[31:2]));

/******************** WRITE STATE MACHINE ********************/
    //define write state
    localparam WRITE_REQ_INIT = 8'h01,
               WRITE_INST_REQ = 8'h02,
               WRITE_DATA_REQ = 8'h04,
               WRITE_DATA = 8'h08,
               WRITE_WAIT = 8'h10;
    //upgrade current writing state
    always @(posedge clk) begin
        if(!resetn)
            w_cur_state <= WRITE_REQ_INIT;
        else
            w_cur_state <= w_next_state;
    end
    //write state machine
    always @(*) begin
        case(w_cur_state)
            WRITE_REQ_INIT: begin
                if(cpu_inst_write_req & awvalid & ~awready)
                    w_next_state = WRITE_INST_REQ;
                else if(cpu_data_write_req & awvalid & ~awready)
                    w_next_state = WRITE_DATA_REQ;
                else if((cpu_inst_write_req | cpu_data_write_req)
                        & awvalid & awready)
                    w_next_state = WRITE_DATA;
                else
                    w_next_state = WRITE_REQ_INIT;
            end
            WRITE_INST_REQ: begin
                if(awvalid & awready)
                    w_next_state = WRITE_DATA;
                else 
                    w_next_state = WRITE_INST_REQ;
            end
            WRITE_DATA_REQ: begin
                if(awvalid & awready)
                    w_next_state = WRITE_DATA;
                else
                    w_next_state = WRITE_DATA_REQ;
            end
            WRITE_DATA: begin
                if(wvalid & wready)
                    w_next_state = WRITE_WAIT;
                else    
                    w_next_state = WRITE_DATA;
            end
            WRITE_WAIT: begin
                if(bvalid & bready)
                    w_next_state = WRITE_REQ_INIT;
                else
                    w_next_state = WRITE_WAIT;
            end
            default:
                w_next_state = WRITE_REQ_INIT;
        endcase
    end

    always @(posedge clk) begin
        if(~resetn) begin
            awid_r <= 4'b0;
            awaddr_r <= 32'b0;
            awsize_r <= 3'b0;
            wdata_r <= 32'b0;
            wstrb_r <= 4'b0;
        end
        else if(w_cur_state[0] & cpu_inst_write_req & awvalid) begin
            awid_r <= 4'b0;
            awaddr_r <= inst_sram_addr;
            awsize_r <= asize_inst;
            wdata_r <= inst_sram_wdata;
            wstrb_r <= inst_sram_wstrb;
        end
        else if(w_cur_state[0] & cpu_data_write_req & awvalid) begin
            awid_r <= 4'b1;
            awaddr_r <= data_sram_addr;
            awsize_r <= asize_data;
            wdata_r <= data_sram_wdata;
            wstrb_r <= data_sram_wstrb;
        end
        else if(w_cur_state[4] & bvalid & bready) begin
            awid_r <= 4'b0;
            awaddr_r <= 32'b0;
            awsize_r <= 3'b0;
            wdata_r <= 32'b0;
            wstrb_r <= 4'b0; 
        end
    end

/******************** WRITE COMBINATIONAL LOGIC ********************/
    //write acquire
    assign awid = {4{w_cur_state[1] | w_cur_state[2]}} & awid_r
                | {4{w_cur_state[0] & cpu_inst_write_req}} & 4'b0
                | {4{w_cur_state[0] & cpu_data_write_req & ~cpu_inst_write_req}} & 4'b1;
    assign awaddr = {32{w_cur_state[1] | w_cur_state[2]}} & awaddr_r
                  | {32{w_cur_state[0] & cpu_inst_write_req}} & inst_sram_addr
                  | {32{w_cur_state[0] & cpu_data_write_req & ~cpu_inst_write_req}} & data_sram_addr;
    assign awlen = 8'b0;
    assign awsize = {3{w_cur_state[1] | w_cur_state[2]}} & awsize_r
                  | {3{w_cur_state[0] & cpu_inst_write_req}} & asize_inst
                  | {3{w_cur_state[0] & cpu_data_write_req & ~cpu_inst_write_req}} & asize_data;
    assign awburst = 2'b01;
    assign awlock = 2'b0;
    assign awcache = 4'b0;
    assign awprot = 3'b0;
    assign awvalid = (cpu_inst_write_req | cpu_data_write_req) 
                   & (w_cur_state[0] | w_cur_state[1] | w_cur_state[2]) & ~write_after_read
                   & ~(arvalid & arready & (arid == 4'b1));
    //write
    assign wid = 4'b1;
    assign wdata = wdata_r;
    assign wstrb = wstrb_r;
    assign wlast = 1'b1;
    assign wvalid = w_cur_state == WRITE_DATA;  
    //write feedback
    assign bready = w_cur_state == WRITE_WAIT & ~(rvalid & rready & (rid == awid_r));
    //judge write after read
    assign write_after_read = (cpu_inst_write_req & (araddr_r[31:2] == inst_sram_addr[31:2]))
                            | (cpu_data_write_req & (araddr_r[31:2] == data_sram_addr[31:2]));

/******************** CPU INTERFACE ********************/
    assign cpu_inst_read_req = inst_sram_req & ~inst_sram_wr;
    assign cpu_inst_write_req = inst_sram_req & inst_sram_wr;
    assign cpu_data_read_req = data_sram_req & ~data_sram_wr;
    assign cpu_data_write_req = data_sram_req & data_sram_wr;
    //to cpu
    assign inst_sram_addr_ok = (cpu_inst_read_req & arvalid & arready & (arid == 4'b0))
                            |  (cpu_inst_write_req & awvalid & awready & (awid == 4'b0));
    assign inst_sram_data_ok = (r_cur_state[3] & rready & rvalid & (arid_r == 4'b0)) 
                            |  (w_cur_state[4] & bready & bvalid & (awid_r == 4'b0));
    assign inst_sram_rdata = rdata;
    assign data_sram_addr_ok = (cpu_data_read_req & arvalid & arready & (arid == 4'b1))
                            |  (cpu_data_write_req & awvalid & awready & (awid == 4'b1));
    assign data_sram_data_ok = (r_cur_state[3] & rready & rvalid & (arid_r == 4'b1))
                            |  (w_cur_state[4] & bready & bvalid & (awid_r == 4'b1));
    assign data_sram_rdata = rdata;

endmodule